/*
 *
 *  * Unidata Platform
 *  * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *  *
 *  * Commercial License
 *  * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *  *
 *  * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 *  * For clarification or additional options, please contact: info@unidata-platform.com
 *  * -------
 *  * Disclaimer:
 *  * -------
 *  * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 *  * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 *  * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 *  * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 *  * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 *
 */

package org.unidata.mdm.data.service.segments.relations.draft;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.unidata.mdm.data.configuration.DataNamespace;
import org.unidata.mdm.data.context.RelationIdentityContext;
import org.unidata.mdm.data.module.DataModule;
import org.unidata.mdm.data.type.draft.DataDraftConstants;
import org.unidata.mdm.data.type.draft.DataDraftOperation;
import org.unidata.mdm.data.type.draft.DataDraftParameters;
import org.unidata.mdm.data.type.draft.DataDraftTags;
import org.unidata.mdm.data.type.keys.RelationKeys;
import org.unidata.mdm.draft.context.DraftUpsertContext;
import org.unidata.mdm.draft.dto.DraftUpsertResult;
import org.unidata.mdm.draft.type.Draft;
import org.unidata.mdm.draft.type.DraftTags;
import org.unidata.mdm.system.type.pipeline.Finish;
import org.unidata.mdm.system.type.pipeline.Start;

/**
 * @author Alexey Tsarapkin
 */
@Component(RelationDraftUpsertFinishExecutor.SEGMENT_ID)
public class RelationDraftUpsertFinishExecutor extends Finish<DraftUpsertContext, DraftUpsertResult> {

    public static final String SEGMENT_ID = DataModule.MODULE_ID + "[RELATION_DRAFT_UPSERT_FINISH]";

    private static final String SEGMENT_DESCRIPTION = DataModule.MODULE_ID + ".relation.draft.upsert.finish.description";

    public RelationDraftUpsertFinishExecutor(){
        super(SEGMENT_ID, SEGMENT_DESCRIPTION, DraftUpsertResult.class);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public DraftUpsertResult finish(DraftUpsertContext ctx) {

        Draft draft = ctx.currentDraft();
        DraftUpsertResult result = new DraftUpsertResult(true);

        boolean reset = BooleanUtils.isTrue(ctx.getFromUserStorage(DataDraftConstants.DRAFT_STATE_RESET));
        boolean setup = !draft.isExisting() || reset;

        // Set subject, if needed (always checked)
        subject(draft, result, ctx);

        if (setup) {
            // 1. Set variables
            variables(draft, result);
            // 2. Update tags
            tags(draft, result, ctx.getPayload(), reset);
        }

        // Regular save runs
        result.setEdition(ctx.currentEdition());
        result.setDraft(draft);

        return result;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean supports(Start<?, ?> start) {
        return DraftUpsertContext.class.isAssignableFrom(start.getInputTypeClass());
    }
    /*
     * Just put the variables to result.
     */
    private void variables(Draft draft, DraftUpsertResult result) {
        result.setVariables(draft.getVariables());
    }
    /*
     * Set subject if not set.
     */
    private void subject(Draft draft, DraftUpsertResult result, DraftUpsertContext ctx) {

        if (StringUtils.isNotBlank(draft.getSubjectId())) {
            return;
        }

        // Support update of subject id for new records,
        // created with this draft (association phase)
        String etalonId = draft.getVariables().valueGet(DataDraftConstants.ETALON_ID);
        if (StringUtils.isBlank(etalonId)) {
            etalonId = ctx.getParameter(DataDraftParameters.SUBJECT_ID);
        }

        if (StringUtils.isNotBlank(etalonId)) {
            result.setSubjectId(etalonId);
        }
    }
    /*
     * Collect user tags and re-initialize system tags.
     */
    private void tags(Draft draft, DraftUpsertResult result, RelationIdentityContext ric, boolean reset) {

        Set<String> retval = new HashSet<>();
        Collection<String> current = Objects.isNull(draft.getTags())
                ? Collections.emptyList()
                : draft.getTags();

        for (String tag : current) {
            if (DataDraftTags.RELATION_SYSTEM_TAGS.stream().noneMatch(tag::startsWith)) {
                retval.add(tag);
            }
        }

        String relationName = draft.getVariables().valueGet(DataDraftConstants.RELATION_NAME);

        retval.add(DraftTags.toTag(DataDraftTags.NAMESPACE, DataNamespace.RELATION.getId()));
        retval.add(DraftTags.toTag(DataDraftTags.ENTITY_NAME, relationName));

        if (reset) {

            RelationKeys keys = ric.relationKeys();

            DataDraftOperation operation = draft.getVariables()
                    .valueGet(DataDraftConstants.OPERATION_CODE, DataDraftOperation.class);

            retval.add(DraftTags.toTag(DataDraftTags.OPERATION_CODE, operation.name()));
            retval.add(DraftTags.toTag(DataDraftTags.RELATION_FROM_ETALON_ID, keys.getEtalonKey().getFrom().getId()));
            retval.add(DraftTags.toTag(DataDraftTags.RELATION_FROM_EXTERNAL_ID, keys.getOriginKey().getFrom().toExternalId().compact()));
            retval.add(DraftTags.toTag(DataDraftTags.RELATION_TO_ETALON_ID, keys.getEtalonKey().getTo().getId()));
            retval.add(DraftTags.toTag(DataDraftTags.RELATION_TO_EXTERNAL_ID, keys.getOriginKey().getTo().toExternalId().compact()));
        }

        result.addTags(retval);
    }
}
